Introduction¶

For this assignment, I will complement Dr. Snow's cholera map by adding new variables and giving more context to the people involved in the tragedy. The purpose is to provide a humane background to the fatalities and bring the user closer to the story.

I will use fictional and real data to differentiate each individual's characteristics and create relationships between them to support the root cause of the problem and eliminate false hypotheses.

Although Dr. Snow's original effort of conveying information was revolutionary, a few extra details will make this analysis easier for the general public.

The fictional data that I will use is: Gender, Age, Name, Profession and Weather category

No description has been provided for this image

In [27]:
pip install geopandas
Collecting geopandas
  Downloading geopandas-1.1.1-py3-none-any.whl.metadata (2.3 kB)
Requirement already satisfied: numpy>=1.24 in /opt/anaconda3/lib/python3.13/site-packages (from geopandas) (2.1.3)
Collecting pyogrio>=0.7.2 (from geopandas)
  Downloading pyogrio-0.11.1-cp313-cp313-macosx_12_0_arm64.whl.metadata (5.3 kB)
Requirement already satisfied: packaging in /opt/anaconda3/lib/python3.13/site-packages (from geopandas) (24.2)
Requirement already satisfied: pandas>=2.0.0 in /opt/anaconda3/lib/python3.13/site-packages (from geopandas) (2.2.3)
Collecting pyproj>=3.5.0 (from geopandas)
  Downloading pyproj-3.7.2-cp313-cp313-macosx_14_0_arm64.whl.metadata (31 kB)
Requirement already satisfied: shapely>=2.0.0 in /opt/anaconda3/lib/python3.13/site-packages (from geopandas) (2.1.2)
Requirement already satisfied: python-dateutil>=2.8.2 in /opt/anaconda3/lib/python3.13/site-packages (from pandas>=2.0.0->geopandas) (2.9.0.post0)
Requirement already satisfied: pytz>=2020.1 in /opt/anaconda3/lib/python3.13/site-packages (from pandas>=2.0.0->geopandas) (2024.1)
Requirement already satisfied: tzdata>=2022.7 in /opt/anaconda3/lib/python3.13/site-packages (from pandas>=2.0.0->geopandas) (2025.2)
Requirement already satisfied: certifi in /opt/anaconda3/lib/python3.13/site-packages (from pyogrio>=0.7.2->geopandas) (2025.8.3)
Requirement already satisfied: six>=1.5 in /opt/anaconda3/lib/python3.13/site-packages (from python-dateutil>=2.8.2->pandas>=2.0.0->geopandas) (1.17.0)
Downloading geopandas-1.1.1-py3-none-any.whl (338 kB)
Downloading pyogrio-0.11.1-cp313-cp313-macosx_12_0_arm64.whl (19.4 MB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 19.4/19.4 MB 691.6 kB/s eta 0:00:0000:0100:01
Downloading pyproj-3.7.2-cp313-cp313-macosx_14_0_arm64.whl (4.6 MB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.6/4.6 MB 727.1 kB/s eta 0:00:00a 0:00:01
Installing collected packages: pyproj, pyogrio, geopandas
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 3/3 [geopandas]/3 [geopandas]
Successfully installed geopandas-1.1.1 pyogrio-0.11.1 pyproj-3.7.2
Note: you may need to restart the kernel to use updated packages.
In [26]:
pip install shapely
Requirement already satisfied: shapely in /opt/anaconda3/lib/python3.13/site-packages (2.1.2)
Requirement already satisfied: numpy>=1.21 in /opt/anaconda3/lib/python3.13/site-packages (from shapely) (2.1.3)
Note: you may need to restart the kernel to use updated packages.
In [24]:
pip install folium
Requirement already satisfied: folium in /opt/anaconda3/lib/python3.13/site-packages (0.20.0)
Requirement already satisfied: branca>=0.6.0 in /opt/anaconda3/lib/python3.13/site-packages (from folium) (0.8.2)
Requirement already satisfied: jinja2>=2.9 in /opt/anaconda3/lib/python3.13/site-packages (from folium) (3.1.6)
Requirement already satisfied: numpy in /opt/anaconda3/lib/python3.13/site-packages (from folium) (2.1.3)
Requirement already satisfied: requests in /opt/anaconda3/lib/python3.13/site-packages (from folium) (2.32.3)
Requirement already satisfied: xyzservices in /opt/anaconda3/lib/python3.13/site-packages (from folium) (2022.9.0)
Requirement already satisfied: MarkupSafe>=2.0 in /opt/anaconda3/lib/python3.13/site-packages (from jinja2>=2.9->folium) (3.0.2)
Requirement already satisfied: charset-normalizer<4,>=2 in /opt/anaconda3/lib/python3.13/site-packages (from requests->folium) (3.3.2)
Requirement already satisfied: idna<4,>=2.5 in /opt/anaconda3/lib/python3.13/site-packages (from requests->folium) (3.7)
Requirement already satisfied: urllib3<3,>=1.21.1 in /opt/anaconda3/lib/python3.13/site-packages (from requests->folium) (2.3.0)
Requirement already satisfied: certifi>=2017.4.17 in /opt/anaconda3/lib/python3.13/site-packages (from requests->folium) (2025.8.3)
Note: you may need to restart the kernel to use updated packages.
In [2]:
import pandas as pd
import folium
import numpy as np
import plotly.express as px
from shapely.geometry import Point, Polygon
from IPython.display import IFrame

Data frame¶

Data frames contain more details about the people affected, like their name, profession, and age. I also included the weather category of the day.

In [3]:
deaths = pd.read_csv("Snow_map_Deaths_Adjusted.csv")
pumps = pd.read_csv("Pumps.csv")
df_weather = deaths.copy()
deaths.head()
Out[3]:
death_count latitude longitude Gender Age Date Name Profession weather_category
0 1 51.513441 -0.138762 Male 13 1854-08-27 Joseph Walker NaN Rainy/Cloudy
1 1 51.512364 -0.137376 Female 25 1854-09-22 Jane Brown Baker Cold
2 1 51.514061 -0.138083 Male 71 1854-08-19 James Brown Old Labourer Sunny
3 1 51.513692 -0.135905 Male 12 1854-09-09 Thomas Clark NaN Rainy/Cloudy
4 1 51.513184 -0.137537 Male 75 1854-09-29 Thomas Jones Retired Sunny
In [3]:
pumps.head()
Out[3]:
pump_name latitude longitude
0 Broad St. 51.513341 -0.136668
1 Crown Chapel 51.513876 -0.139586
2 Gt Marlborough 51.514906 -0.139671
3 Dean St. 51.512354 -0.131630
4 So Soho 51.512139 -0.133594
In [23]:
df_weather.head()
Out[23]:
death_count latitude longitude Gender Age Date Name Profession weather_category
0 1 51.513441 -0.138762 Male 13 1854-08-27 Joseph Walker NaN Rainy/Cloudy
1 1 51.512364 -0.137376 Female 25 1854-09-22 Jane Brown Baker Cold
2 1 51.514061 -0.138083 Male 71 1854-08-19 James Brown Old Labourer Sunny
3 1 51.513692 -0.135905 Male 12 1854-09-09 Thomas Clark NaN Rainy/Cloudy
4 1 51.513184 -0.137537 Male 75 1854-09-29 Thomas Jones Retired Sunny
In [4]:
def age_group(age):
    if age < 20:
        return "<20"
    elif age < 60:
        return "20–59"
    else:
        return "60+"

deaths["AgeGroup"] = deaths["Age"].apply(age_group)
deaths.tail()
Out[4]:
death_count latitude longitude Gender Age Date Name Profession weather_category AgeGroup
482 1 51.512447 -0.137656 Female 7 1854-08-24 Elizabeth Jones NaN Cold <20
483 1 51.513046 -0.137562 Female 74 1854-09-22 Emma Smith Old Labourer Cold 60+
484 1 51.513116 -0.138337 Male 74 1854-09-30 Henry Brown Old Labourer Sunny 60+
485 1 51.513692 -0.135905 Male 78 1854-09-27 James Smith Retired Rainy/Cloudy 60+
486 1 51.513418 -0.137930 Female 12 1854-09-10 Charlotte Walker Child Sunny <20

Scatter and Area Graphs¶

Using a scatter graph and an area graph, I will show groups of people most vulnerable to the disease. In this case, the profession doesn't show a direct influence, but more so for elderly people and young children.

In [5]:
grouped = deaths.groupby(["Gender", "AgeGroup", "Profession"]).size().reset_index(name="Count")

fig = px.scatter(
    grouped,
    x="AgeGroup",
    y="Profession",
    size="Count",
    color="Gender",
    hover_name="Profession",
    size_max=60,
    color_discrete_map={"Male": "blue", "Female": "red"},
    title= "Deaths by Age Group, Gender, and Profession",
)


fig.for_each_trace(
    lambda trace: trace.update(
        marker=dict(
            opacity=0.4 if trace.name == "Male" else 0.4,
            line=dict(width=1, color="DarkSlateGrey"))))

fig.update_layout(
    plot_bgcolor="white",
    xaxis=dict(showgrid=True, gridcolor="lightgrey"),
    yaxis=dict(showgrid=True, gridcolor="lightgrey"),
    height=700,
)

fig.show()
In [11]:
from IPython.display import IFrame
url = "https://datawrapper.dwcdn.net/ClGV8/2/"
IFrame(src=url, width="100%", height=500)
Out[11]:

Note¶

No profession shows a tendency of being aligned with cholera deaths

Weather¶

I will show the weather each day to see if that influenced the number of deaths. It is known that hot weather means higher dehydration, increased body heat, and discomfort, making it more likely for people to perish.

In [7]:
# Column detection 
def find_col(df, keyword):
    matches = [c for c in df.columns if keyword in c.lower()]
    return matches[0] if matches else None

date_col = find_col(df_weather, "date")
gender_col = find_col(df_weather, "gender")
age_col = find_col(df_weather, "age")
weather_col = find_col(df_weather, "weather")

# Prepare data 
df_weather[date_col] = pd.to_datetime(df_weather[date_col], errors="coerce")
df_weather = df_weather.dropna(subset=[date_col, gender_col, age_col])

# Sort females then males
df_weather = pd.concat([
    df_weather[df_weather[gender_col].str.lower().str.startswith("f")].sort_values(by=age_col),
    df_weather[df_weather[gender_col].str.lower().str.startswith("m")].sort_values(by=age_col)
], ignore_index=True)

# Colors 
RED = "\033[91m"      
BLUE = "\033[94m"    
GRAY = "\033[90m"     
RESET = "\033[0m"

# Shape age 
def get_shape(age):
    if age < 15:
        return "●"  
    elif age < 60:
        return "▲"   
    else:
        return "■"  

def get_symbol(gender, age):
    shape = get_shape(age)
    color = RED if gender.lower().startswith("f") else BLUE
    return f"{color}{shape}{RESET} "

df_weather["symbol"] = df_weather.apply(lambda r: get_symbol(r[gender_col], r[age_col]), axis=1)

# Weather icons 
weather_icons = {"Sunny": "☀️", "Rainy/Cloudy": "☁️", "Cold": "❄️"}

# daily  
grouped = df_weather.groupby(date_col)
max_deaths = grouped.size().max()

print("\nGRID OF DEATHS PER DAY WITH WEATHER ICON\n")
print("Legend:")
print(f"{RED}●{RESET} Child Female   {RED}▲{RESET} Adult Female   {RED}■{RESET} Elderly Female")
print(f"{BLUE}●{RESET} Child Male     {BLUE}▲{RESET} Adult Male     {BLUE}■{RESET} Elderly Male")
print(f"{GRAY}·{RESET} Empty Slot")
print("☀️  Sunny   ☁️  Rainy/Cloudy   ❄️  Cold\n")

rows = []
for date, group in grouped:
    symbols = list(group["symbol"])
    count = len(symbols)
    if count < max_deaths:
        symbols.extend([f"{GRAY}·{RESET} "] * (max_deaths - count))
    row_string = "".join(symbols)
    weather_val = df_weather.loc[df_weather[date_col] == date, weather_col].iloc[0]
    weather_icon = weather_icons.get(weather_val, "☁️")
    rows.append((date.strftime("%d/%m/%y"), weather_icon, row_string, weather_val))

max_label_len = max(len(r[0]) for r in rows)
for label, wicon, row, _ in rows:
    print(f"{label:<{max_label_len}} {wicon}  {row}")

#  Weather summary 
death_summary = df_weather.groupby(weather_col).size().reindex(["Sunny", "Rainy/Cloudy", "Cold"], fill_value=0)
unique_weather_days = df_weather[[date_col, weather_col]].drop_duplicates().groupby(weather_col).size().reindex(["Sunny", "Rainy/Cloudy", "Cold"], fill_value=0)
ratio = (death_summary / unique_weather_days).round(2)

print("\nWEATHER DAY COUNT\n")
for weather, days in unique_weather_days.items():
    icon = weather_icons.get(weather, "")
    print(f"{icon}  {weather:<15}: {days:>5} days")

print("\nWEATHER SUMMARY (Total Deaths)\n")
for weather, count in death_summary.items():
    icon = weather_icons.get(weather, "")
    print(f"{icon}  {weather:<15}: {count:>5} deaths")

print("\nWEATHER IMPACT ANALYSIS (Deaths / Day)\n")
for weather in ["Sunny", "Rainy/Cloudy", "Cold"]:
    icon = weather_icons.get(weather, "")
    print(f"{icon}  {weather:<15}: {ratio[weather]:>5} deaths/day over {unique_weather_days[weather]} days")
print("\n")
GRID OF DEATHS PER DAY WITH WEATHER ICON

Legend:
● Child Female   ▲ Adult Female   ■ Elderly Female
● Child Male     ▲ Adult Male     ■ Elderly Male
· Empty Slot
☀️  Sunny   ☁️  Rainy/Cloudy   ❄️  Cold

19/08/54 ☀️  ● ● ● ● ● ● ▲ ● ● ● ▲ ▲ ▲ ▲ ■ ■ ■ ■ ■ ■ ■ ■ · · · · 
20/08/54 ☁️  ● ● ▲ ■ ■ ■ ▲ · · · · · · · · · · · · · · · · · · · 
21/08/54 ☁️  ● · · · · · · · · · · · · · · · · · · · · · · · · · 
22/08/54 ☀️  ● ▲ ▲ ▲ ● ▲ ▲ ▲ ■ ■ ■ ■ · · · · · · · · · · · · · · 
23/08/54 ❄️  ■ ■ ■ ● ● ● ● ● ▲ ▲ ▲ · · · · · · · · · · · · · · · 
24/08/54 ❄️  ● ● ● ● ▲ ▲ ▲ ▲ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ▲ ■ ■ ■ ■ ■ 
25/08/54 ❄️  ● ● ▲ ■ ■ ■ ■ ● ▲ ▲ ■ ■ ■ ■ ■ · · · · · · · · · · · 
26/08/54 ☁️  ● ● ● ● ● ■ ■ ■ ■ ■ ■ · · · · · · · · · · · · · · · 
27/08/54 ☁️  ● ▲ ■ ■ ● ● ● ▲ ▲ ■ ■ ■ ■ · · · · · · · · · · · · · 
28/08/54 ☀️  ● ▲ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ · · · · · · · · · · · · 
29/08/54 ❄️  ● ● ▲ ● ▲ · · · · · · · · · · · · · · · · · · · · · 
30/08/54 ☁️  ▲ ■ ■ ■ ■ ● ● ▲ ▲ · · · · · · · · · · · · · · · · · 
31/08/54 ☁️  ● ▲ ▲ ▲ ■ ■ ● ● ■ ■ ■ ■ ■ · · · · · · · · · · · · · 
01/09/54 ☁️  ■ ■ ■ ■ ■ ● ▲ ■ ■ ■ ■ ■ · · · · · · · · · · · · · · 
02/09/54 ☁️  ● ● ● ▲ ▲ ■ ■ ■ ■ ■ ▲ ▲ ■ · · · · · · · · · · · · · 
03/09/54 ❄️  ● ● ● ▲ ▲ ▲ ● ▲ ▲ · · · · · · · · · · · · · · · · · 
04/09/54 ☁️  ● ▲ ▲ ▲ ■ ■ ■ ■ ● ▲ ▲ ■ ■ ■ ■ ■ · · · · · · · · · · 
05/09/54 ☁️  ▲ ■ ■ ■ ▲ ■ ■ ■ ■ · · · · · · · · · · · · · · · · · 
06/09/54 ☁️  ■ ● ▲ ▲ ■ ■ · · · · · · · · · · · · · · · · · · · · 
07/09/54 ☀️  ● ▲ ● ● ● ● ▲ ▲ ▲ ▲ ▲ ■ ■ · · · · · · · · · · · · · 
08/09/54 ☁️  ● ● ● ▲ ▲ · · · · · · · · · · · · · · · · · · · · · 
09/09/54 ☁️  ● ● ● ▲ ▲ ▲ · · · · · · · · · · · · · · · · · · · · 
10/09/54 ☀️  ● ● ● ● ● ● ● ● ▲ ■ ■ ■ ■ · · · · · · · · · · · · · 
11/09/54 ❄️  ● ▲ ▲ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ▲ ▲ ▲ ■ ■ ■ · · · · 
12/09/54 ☁️  ● ▲ ▲ · · · · · · · · · · · · · · · · · · · · · · · 
13/09/54 ❄️  ● ● ● ● ▲ ● ● ▲ · · · · · · · · · · · · · · · · · · 
14/09/54 ☁️  ● ● ● ▲ ▲ ▲ ▲ ▲ ● ● ▲ ▲ ▲ ▲ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ · 
15/09/54 ☁️  ● ▲ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ · · · · · · · · · · 
16/09/54 ❄️  ● ● ▲ ▲ ▲ ▲ ■ ■ ■ ■ ■ ■ ▲ · · · · · · · · · · · · · 
17/09/54 ☁️  ● ▲ ▲ ▲ ▲ ▲ ▲ ■ ■ ■ ■ ■ · · · · · · · · · · · · · · 
18/09/54 ☀️  ● ● ● ● ▲ ■ ■ ■ ■ ▲ ▲ ▲ · · · · · · · · · · · · · · 
19/09/54 ☁️  ▲ · · · · · · · · · · · · · · · · · · · · · · · · · 
20/09/54 ☀️  ● ▲ ● ▲ ▲ · · · · · · · · · · · · · · · · · · · · · 
21/09/54 ☁️  ▲ ■ ■ ● ● ● ● ▲ ▲ ▲ · · · · · · · · · · · · · · · · 
22/09/54 ❄️  ▲ ■ ■ ■ · · · · · · · · · · · · · · · · · · · · · · 
23/09/54 ☁️  ● ▲ ▲ ● ● ● ● ● ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ · · · · · · · · 
24/09/54 ☁️  ● ● ● ▲ ▲ ▲ ▲ ▲ ■ ■ ■ · · · · · · · · · · · · · · · 
25/09/54 ☁️  ● ● ▲ ▲ ▲ ■ ■ ■ ● ● · · · · · · · · · · · · · · · · 
26/09/54 ❄️  ● ● ■ ■ ■ ■ ■ · · · · · · · · · · · · · · · · · · · 
27/09/54 ☁️  ■ ■ ● ▲ ▲ ▲ ▲ ▲ ▲ ■ ■ ■ ■ ■ ■ · · · · · · · · · · · 
28/09/54 ❄️  ▲ ■ ■ ■ ■ ■ ■ ■ ■ ▲ ■ ■ · · · · · · · · · · · · · · 
29/09/54 ☀️  ● ▲ ▲ ■ ■ ■ ■ ■ ■ ■ ■ ■ · · · · · · · · · · · · · · 
30/09/54 ☀️  ● ▲ ▲ ▲ ● ■ ■ ■ ■ ■ · · · · · · · · · · · · · · · · 

WEATHER DAY COUNT

☀️  Sunny          :     9 days
☁️  Rainy/Cloudy   :    23 days
❄️  Cold           :    11 days

WEATHER SUMMARY (Total Deaths)

☀️  Sunny          :   113 deaths
☁️  Rainy/Cloudy   :   242 deaths
❄️  Cold           :   132 deaths

WEATHER IMPACT ANALYSIS (Deaths / Day)

☀️  Sunny          : 12.56 deaths/day over 9 days
☁️  Rainy/Cloudy   : 10.52 deaths/day over 23 days
❄️  Cold           :  12.0 deaths/day over 11 days


Mapping the disease¶

After determining who is more likely to perish from the disease, I will locate each individual using their characteristics to redesign the map and confirm that gender and age are not the primary causes of contracting the disease.

I will also add each character's name to make sure that the user does not forget that these are not numbers, but people with real lives.

In [5]:
deaths["Date"] = pd.to_datetime(deaths["Date"], errors="coerce")
#Color and shape setup
gender_color = {"Male": "blue", "Female": "pink"}
shape_map = {"<20": "circle", "20–59": "triangle", "60+": "square"}

# Create base map
m = folium.Map(location=[51.5133, -0.1368], zoom_start=17, tiles="CartoDB Positron")

# Add pumps 
for _, row in pumps.iterrows():
    folium.Marker(
        location=[row["latitude"], row["longitude"]],
        popup=f"<b>Pump:</b> {row['pump_name']}",
        tooltip=row["pump_name"],
        icon=folium.Icon(color="blue", icon="flag", prefix="fa", icon_color="white")
    ).add_to(m)

# Add deaths
for _, row in deaths.dropna(subset=["latitude", "longitude"]).iterrows():
    color = gender_color.get(row["Gender"], "gray")
    shape = shape_map.get(row["AgeGroup"], "circle")
    opacity = 0.6 if row["Gender"] == "Male" else 0.4

    tooltip_text = (
        f"<b>Name:</b> {row['Name']}<br>"
        f"<b>Gender:</b> {row['Gender']}<br>"
        f"<b>Age:</b> {row['Age']}<br>"
        f"<b>Profession:</b> {row['Profession']}<br>"
        f"<b>Date of Death:</b> {row['Date'].strftime('%Y-%m-%d') if pd.notnull(row['Date']) else 'Unknown'}"
    )

    if shape == "circle":
        folium.CircleMarker(
            location=[row["latitude"], row["longitude"]],
            radius=5,
            color=color,
            fill=True,
            fill_color=color,
            fill_opacity=opacity,
            tooltip=tooltip_text
        ).add_to(m)
    elif shape == "triangle":
        folium.RegularPolygonMarker(
            location=[row["latitude"], row["longitude"]],
            number_of_sides=3,
            radius=6,
            color=color,
            fill=True,
            fill_color=color,
            fill_opacity=opacity,
            tooltip=tooltip_text
        ).add_to(m)
    else:
        folium.RegularPolygonMarker(
            location=[row["latitude"], row["longitude"]],
            number_of_sides=4,
            radius=6,
            color=color,
            fill=True,
            fill_color=color,
            fill_opacity=opacity,
            tooltip=tooltip_text
        ).add_to(m)

# Legend 
legend_html = """
<div style="
  position: fixed;
  bottom: 30px; right: 20px;
  width: 220px; background-color: white;
  border: 2px solid grey; z-index:9999; padding: 10px;
">
<b>Legend</b><br>
Gender:<br>
<span style='color:blue;font-size:18px;'>●</span> Male<br>
<span style='color:pink;font-size:18px;'>●</span> Female<br><br>
Age Groups:<br>
◯ &lt;20 years<br>
▲ 20–59 years<br>
■ 60+ years<br><br>
<span style='color:blue;font-size:18px;'>⚑</span> Water Pump
</div>
"""


m
Out[5]:
Make this Notebook Trusted to load map: File -> Trust Notebook

Conclusion¶

This map shows us something similar to what Dr. Snow created, but it gives people a presence and voice inside the tragedy. It is important to maintain connection in what we show. Data by itself creates an impact, but empathy is what influences the spectator. Most of the time, we solemnly focus on getting the point across, but it depends on us to be understood.

Bonus Map¶

In [ ]:
 
In [3]:
from IPython.display import HTML

HTML("""
<div class="flourish-embed flourish-map" data-src="visualisation/25855341">
  <script src="https://public.flourish.studio/resources/embed.js"></script>
  <noscript><img src="https://public.flourish.studio/visualisation/25855341/thumbnail" width="100%" alt="map visualization" /></noscript>
</div>
""")
Out[3]: